package com.tang.param_check.aspect;

import com.tang.param_check.aspect.annotation.ParamCheck;
import com.tang.param_check.aspect.handler.HandlerManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Objects;

/**
 * 参数校验的切面类，校验所有的controller的数据
 *
 * @Author tangyuan2meicloud.com
 * @DATE 2021/12/17 20:24
 * @Desc
 * @Remarks
 */
@Slf4j
@Aspect
//@Component
public class ParamAspect {

    @Autowired
    private HandlerManager handlerManager;

    @Pointcut("execution(* com.tang..*.controller..*.*(..))")
    public void Pointcut() {
    }

    /**
     * @param point
     * @throws Exception
     */
    @Before("Pointcut()")
    public void beforeMethod(JoinPoint point) {
        Object[] args = point.getArgs();
        boolean match = checkDomain(args);
        if (!match) {
            checkCollection(point, args);
        }
    }

    /**
     * 单类的问题
     */
    private boolean checkDomain(Object[] args) {
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                Class<?> aClass = arg.getClass();
                if (aClass.isAnnotationPresent(ParamCheck.class)) {
                    checkSignalParam(arg, aClass);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 集合的问题
     */
    private void checkCollection(JoinPoint point, Object[] args) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        method.setAccessible(true);
        Parameter[] parameters = method.getParameters();

        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object arg = args[i];
            if (arg instanceof Collection) {
                Type genericType = parameter.getParameterizedType();
                if (genericType == null) continue;
                // 如果是泛型参数的类型
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    Type[] actualTypeArguments = pt.getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        Class genericTypeClazz = (Class<?>) actualTypeArgument;
                        if (genericTypeClazz.isAnnotationPresent(ParamCheck.class)) {
                            checkCollectionParam((Collection) arg, genericTypeClazz);
                        }
                    }
                }
            }
        }
    }

    /**
     * 处理集合中定义了@ParamCheck的参数
     *
     * @param arg
     * @param genericTypeClazz
     */
    private void checkCollectionParam(Collection arg, Class<?> genericTypeClazz) {
        log.info("集合的参数校验，arg：{},class:{}", arg, genericTypeClazz);
        ParamCheck annotation = genericTypeClazz.getAnnotation(ParamCheck.class);
        Class clazz = annotation.handleClass();
        if (Objects.isNull(clazz)) {
            log.info(genericTypeClazz.getName() + "定义的@ParamCheck无处理器! ");
            throw new RuntimeException("参数校验处理失败！");
        }
        if (handlerManager.handleCollection(clazz, arg)) {
            log.info("集合参数校验处理成功");
        } else {
            throw new RuntimeException("参数校验处理失败！");
        }
    }

    /**
     * 处理直接类上定义了@ParamCheck的参数
     *
     * @param arg
     * @param aClass
     */
    private void checkSignalParam(Object arg, Class<?> aClass) {
        log.info("单个的参数校验");
        ParamCheck annotation = aClass.getAnnotation(ParamCheck.class);
        Class clazz = annotation.handleClass();

        if (Objects.isNull(clazz)) {
            log.info(aClass.getName() + "定义的@ParamCheck无处理器! ");
            throw new RuntimeException("参数校验处理失败！");
        }
        if (handlerManager.handle(clazz, arg)) {
            log.info("集合参数校验处理成功");
        } else {
            throw new RuntimeException("参数校验处理失败！");
        }
    }

}
